home *** CD-ROM | disk | FTP | other *** search
/ Windows Expert / Windows Expert.iso / utility / uwserver.zip / uwserver.tar / server / uw_pcl.c < prev    next >
C/C++ Source or Header  |  1991-01-25  |  22KB  |  926 lines

  1. /*
  2.  *    uw_pcl - protocol handling for UW
  3.  *
  4.  * Copyright 1985,1986 by John D. Bruner.  All rights reserved.  Permission to
  5.  * copy this program is given provided that the copy is not sold and that
  6.  * this copyright notice is included.
  7.  */
  8.  
  9. #include <sys/types.h>
  10. #include <sys/ioctl.h>
  11. #include <errno.h>
  12.  
  13. #include "uw_param.h"
  14. #include "uw_clk.h"
  15. #include "uw_opt.h"
  16. #include "uw_win.h"
  17. #include "uw_pcl.h"
  18. #include "openpty.h"
  19.  
  20. #define    XON    0021            /* ASCII XON */
  21. #define    XOFF    0023            /* ASCII XOFF */
  22. #define    RUB    0177            /* ASCII RUBout */
  23. #define    META    0200            /* "meta" bit for whatever it's worth */
  24.  
  25. /*
  26.  * Protocol negotiation is performed by a finite state machine (implemented
  27.  * in pcl_haggle()).  The states are defined as the enumerated type
  28.  * "pnstate_t".  The inputs have type "pnreq_t".
  29.  */
  30. typedef enum {
  31.     PNST_NOP,            /* no protocol negotiation */
  32.     PNST_AWAIT,            /* timing out an ASKPCL */
  33.     PNST_CWAIT,            /* timing out a CANPCL */
  34.     PNST_OK,            /* negotiations completed */
  35.     PNST_FAIL            /* negotiation failed */
  36. } pnstate_t;
  37.  
  38. typedef unsigned short pnreq_t;        /* finite state machine requests */
  39. #define    PNRQ_PCL    0000377        /* protocol mask */
  40. #define    PNRQ_CMD    0177400        /* command mask: */
  41. #define    PNRQ_NONE    (pnreq_t)(0<<8)    /*    no request */
  42. #define    PNRQ_START    (pnreq_t)(1<<8)    /*    start negotiation */
  43. #define    PNRQ_AWAIT    (pnreq_t)(2<<8)    /*    timeout waiting for ASKPCL */
  44. #define    PNRQ_ASK    (pnreq_t)(3<<8)    /*    process received ASKPCL */
  45. #define    PNRQ_CAN    (pnreq_t)(4<<8)    /*    process received CANPCL */
  46. #define    PNRQ_SET    (pnreq_t)(5<<8)    /*    process received SETPCL */
  47. #define    PNRQ_CWAIT    (pnreq_t)(6<<8)    /*    timeout waiting for CANPCL */
  48. #define    PNRQ_INIT    (pnreq_t)(7<<8)    /*    initialize everything */
  49.  
  50. static int p1_ctlch[] = { -1, P1_IAC, XON, XOFF, -1, -1, -1, -1 };
  51.  
  52. extern void p1_entry(), p1_renew(), p2_renew();
  53. extern struct window *p1_neww(), *p2_neww();
  54. extern void p1_killw(), p1_xmit(), p1_recv();
  55. extern void p1_askpcl(), p1_canpcl(), p1_setpcl();
  56. extern void p2_recv(), p2_chkopt(), p2_sendopt();
  57.  
  58. static struct protocol pcl_table[] = {
  59.     {
  60.       ' ',
  61.       P1_NWINDOW,
  62.       p1_ctlch, sizeof p1_ctlch / sizeof p1_ctlch[0],
  63.       p1_entry, NULL, p1_renew,
  64.       p1_neww, p1_killw,
  65.       p1_xmit, p1_recv, NULL, NULL,
  66.       p1_askpcl, p1_canpcl, p1_setpcl
  67.     },
  68.     {
  69.       '!',
  70.       P2_NWINDOW,
  71.       p1_ctlch, sizeof p1_ctlch / sizeof p1_ctlch[0],
  72.       p1_entry, NULL, p2_renew,
  73.       p2_neww, p1_killw,
  74.       p1_xmit, p2_recv, p2_chkopt, p2_sendopt,
  75.       p1_askpcl, p1_canpcl, p1_setpcl
  76.     },
  77. };
  78.  
  79. struct protocol *protocol = pcl_table;
  80.  
  81. /*
  82.  * Two "current" windows are defined: the current input window (for
  83.  * input from the Macintosh) and the current output window (for output
  84.  * to the Macintosh).
  85.  */
  86. static struct {
  87.     struct window    *in;
  88.     struct window    *out;
  89. } curwin;
  90.  
  91.  
  92. pcl_entry(mfd)
  93. register fildes_t mfd;
  94. {
  95.     /*
  96.      * This routine is called to start up protocol handling.  We always
  97.      * start with protocol 1 (the original UW protocol).
  98.      */
  99.     protocol = pcl_table;
  100.     pcl_haggle(mfd, PNRQ_INIT);
  101.     if (protocol->p_entry)
  102.         (*protocol->p_entry)(mfd);
  103. }
  104.  
  105. pcl_exit(mfd)
  106. register fildes_t mfd;
  107. {
  108.     /*
  109.      * This routine is called when we shut down (just before the server
  110.      * exits).
  111.      */
  112.     if (protocol->p_exit)
  113.         (*protocol->p_exit)(mfd);
  114.     protocol = pcl_table;
  115. }
  116.  
  117. static
  118. pcl_newpcl(newproto)
  119. struct protocol *newproto;
  120. {
  121.     extern void rc_kludge();
  122.  
  123.     /*
  124.      * Switch to new protocol "newproto".  Right now we can get away
  125.      * with just changing the value of "protocol".  Eventually we will
  126.      * probably want to call protocol-dependent functions to shut down
  127.      * the old protocol and start up the new one.
  128.      */
  129.     protocol = newproto;
  130.  
  131.     /*
  132.      * This is a horrible kludge.  See rc_kludge() in "main.c" for
  133.      * further details.
  134.      */
  135.     rc_kludge();
  136. }
  137.  
  138. static
  139. void
  140. pcl_tohaggle(arg)
  141. register toarg_t arg;
  142. {
  143.     /*
  144.      * This is a kludge to get around the single-argument restriction
  145.      * on clk_timeout.  We split the argument "arg" into two 16-bit
  146.      * pieces and invoke pcl_haggle.
  147.      */
  148.     pcl_haggle((fildes_t)((arg>>16)&0177777), (pnreq_t)(arg&0177777));
  149. }
  150.  
  151. static
  152. pcl_haggle(mfd, req)
  153. fildes_t mfd;
  154. register pnreq_t req;
  155. {
  156.     register struct protocol *p, *q;
  157.     register char pname;
  158.     register int request;
  159.     static pnstate_t pnstate;
  160.     static int waitcnt;
  161.  
  162.     /*
  163.      * This routine implements the finite-state machine that handles
  164.      * protocol negotiation.  This routine is called by routines which
  165.      * recognize incoming protocol commands and at 5 second intervals
  166.      * when negotiations are in progress.  The current protocol is
  167.      * described by the variable "protocol".
  168.      */
  169.     if (req == PNRQ_INIT) {
  170.         waitcnt = 0;
  171.         pnstate = PNST_NOP;
  172.         req = PNRQ_NONE;
  173.     }
  174.     if (!(p = protocol) || !p->p_askpcl || !p->p_canpcl || !p->p_setpcl) {
  175.         req = PNRQ_NONE;
  176.     } else {
  177.         pname = req & PNRQ_PCL;
  178.         request = req & PNRQ_CMD;
  179.         switch (request) {
  180.         case PNRQ_START:    /* start protocol negotiation */
  181.             /*
  182.              * The Macintosh is responsible for starting protocol
  183.              * negotiation (if it wants something other than the
  184.              * standard protocol).  This code is present for
  185.              * purposes of exposition only.
  186.              */
  187.             (*p->p_askpcl)(mfd);
  188.             req = PNRQ_AWAIT | pname;
  189.             waitcnt = 0;
  190.             pnstate = PNST_AWAIT;
  191.             break;
  192.         case PNRQ_AWAIT:    /* timeout an ASKPCL */
  193.             /*
  194.              * This state also is not reached on the host.
  195.              */
  196.             if (pnstate == PNST_AWAIT) {
  197.                 if (++waitcnt > 3) {
  198.                     pnstate = PNST_FAIL;
  199.                     req = PNRQ_NONE;
  200.                 } else
  201.                     (*p->p_askpcl)(mfd);
  202.             } else
  203.                 req = PNRQ_NONE;
  204.             break;
  205.         case PNRQ_ASK:        /* handle received ASKPCL */
  206.             q = pcl_table+sizeof pcl_table/sizeof pcl_table[0] - 1;
  207.             (*p->p_canpcl)(mfd, q->p_name);
  208.             pnstate = PNST_CWAIT;
  209.             req = PNRQ_CWAIT | q->p_name;
  210.             waitcnt = 0;
  211.             break;
  212.         case PNRQ_CAN:        /* handle received CANPCL */
  213.             for (q=pcl_table+sizeof pcl_table/sizeof pcl_table[0]-1;
  214.                  q > pcl_table && q->p_name > pname;
  215.                  q--)
  216.                 ;
  217.             if (q->p_name == pname || q == pcl_table) {
  218.                 (*p->p_setpcl)(mfd, q->p_name);
  219.                 pcl_newpcl(q);
  220.                 pnstate = PNST_OK;
  221.                 req = PNRQ_NONE;
  222.             } else {
  223.                 (*p->p_canpcl)(mfd, q->p_name);
  224.                 pnstate = PNST_CWAIT;
  225.                 req = PNRQ_CWAIT | q->p_name;
  226.                 waitcnt = 0;
  227.             }
  228.             break;
  229.         case PNRQ_CWAIT:    /* timeout a CANPCL */
  230.             if (pnstate == PNST_CWAIT) {
  231.                 if (++waitcnt > 3) {
  232.                     pnstate = PNST_FAIL;
  233.                     req = PNRQ_NONE;
  234.                 } else
  235.                     (*p->p_canpcl)(mfd, pname);
  236.             } else
  237.                 req = PNRQ_NONE;
  238.             break;
  239.         case PNRQ_SET:        /* handle a received SETPCL */
  240.             for (q=pcl_table+sizeof pcl_table/sizeof pcl_table[0]-1;
  241.                  q > pcl_table && q->p_name != pname;
  242.                  q--)
  243.                 ;
  244.             if (q->p_name == pname) {
  245.                 pcl_newpcl(q);
  246.                 pnstate = PNST_OK;
  247.                 req = PNRQ_NONE;
  248.             } else {
  249.                 /*
  250.                  * We are in trouble now -- the Mac has
  251.                  * instructed us to switch to a protocol
  252.                  * that we can't support.  We switch back
  253.                  * to protocol 1 and hope that our message
  254.                  * to the Mac (telling it to switch to
  255.                  * protocol 1) will be interpreted correctly.
  256.                  */
  257.                 pnstate = PNST_FAIL;
  258.                 req = PNRQ_NONE;
  259.                 (*p->p_setpcl)(mfd, ' ');
  260.                 if (p != pcl_table)
  261.                     pcl_newpcl(pcl_table);
  262.             }
  263.             break;
  264.         }
  265.         if (req != PNRQ_NONE)
  266.             (void)clk_timeout(5*CLK_HZ,
  267.                 pcl_tohaggle, (toarg_t)(((long)mfd<<16)|req));
  268.     }
  269. }
  270.  
  271. static
  272. void
  273. p1_entry(mfd)
  274. fildes_t mfd;
  275. {
  276.     static char cmdbuf[2] = { P1_IAC };
  277.  
  278.     cmdbuf[1] = P1_DIR_HTOM|P1_FN_MAINT|P1_MF_ENTRY;
  279.     (void)write(mfd, cmdbuf, sizeof cmdbuf);
  280. }
  281.  
  282. static
  283. struct window *
  284. p1_neww(mfd, wclass, wtype, wnum, wid, datafd, ctlfd)
  285. fildes_t mfd;
  286. wclass_t wclass;
  287. wtype_t wtype;
  288. nwin_t wnum;
  289. long wid;
  290. fildes_t datafd;
  291. fildes_t ctlfd;
  292. {
  293.     register struct window *w;
  294.     static char cmdbuf[2] = { P1_IAC, 0 };
  295.  
  296.     /*
  297.      * Create a new window for the host.  This routine is not called when
  298.      * the Macintosh creates a window.
  299.      */
  300.     w = win_neww(wclass, wtype, wnum, protocol->p_maxwin, wid,
  301.         datafd, ctlfd, (struct woptdefn *)0);
  302.     if (w) {
  303.         cmdbuf[1] = P1_DIR_HTOM|P1_FN_NEWW|WIN_NUM(w);
  304.         (void)write(mfd, cmdbuf, sizeof cmdbuf);
  305.     }
  306.     return(w);
  307. }
  308.  
  309. static
  310. void
  311. p1_killw(mfd, w)
  312. register struct window *w;
  313. {
  314.     static char cmdbuf[] = { P1_IAC, P1_DIR_HTOM|P1_FN_KILLW };
  315.  
  316.     /*
  317.      * Kill window "w" and tell the Macintosh to do the same.
  318.      */
  319.     if (w && w->w_alloc) {
  320.         if (curwin.in == w)
  321.             curwin.in = (struct window *)0;
  322.         if (curwin.out == w)
  323.             curwin.out = (struct window *)0;
  324.         cmdbuf[1] = P1_DIR_HTOM|P1_FN_KILLW|WIN_NUM(w);
  325.         (void)write(mfd, cmdbuf, sizeof cmdbuf);
  326.         win_killw(w);
  327.     }
  328. }
  329.  
  330. static
  331. void
  332. p1_xmit(mfd, w)
  333. fildes_t mfd;
  334. register struct window *w;
  335. {
  336.     register char *cp, *cq;
  337.     register int i, len;
  338.     char ibuf[32], obuf[32];
  339.     static char refresh;
  340.     static char cmdbuf[] = { P1_IAC, 0 };
  341.     extern int errno;
  342.  
  343.     /*
  344.      * Transmit data to the Macintosh (via file descriptor "mfd)
  345.      * on behalf of window "w".  Be sure to convert any embedded
  346.      * control characters and meta characters.
  347.      *
  348.      * Note that the input/output buffers should NOT be very large.
  349.      * It is undesirable to perform large reads and effectively
  350.      * "lock out" all other file descriptors.  The chosen size
  351.      * should preserve a reasonable amount of efficiency.
  352.      *
  353.      * The UW protocol only requires an OSELW command when the
  354.      * output window changes.  We issue this command more often
  355.      * to "refresh" the Mac's idea of what the output window is.
  356.      * This helps (slightly) to overcome spurious output redirects
  357.      * caused by a noisy line.
  358.      */
  359.     if (w && w->w_alloc) {
  360.         if (curwin.out != w || ++refresh == 0) {
  361.             refresh = 0;
  362.             curwin.out = w;
  363.             cmdbuf[1] = P1_DIR_HTOM|P1_FN_OSELW|WIN_NUM(w);
  364.             (void)write(mfd, cmdbuf, sizeof cmdbuf);
  365.         }
  366.         cq = obuf;
  367.         if ((len = read(w->w_datafd, ibuf, sizeof ibuf)) < 0 &&
  368.             errno != EWOULDBLOCK)
  369.             (*protocol->p_killw)(mfd, w);
  370.         for (cp=ibuf; cp < ibuf+len; cp++) {
  371.             if (*cp&META) {
  372.                 if (cq > obuf) {
  373.                     (void)write(mfd, obuf, cq-obuf);
  374.                     cq = obuf;
  375.                 }
  376.                 cmdbuf[1] = P1_DIR_HTOM|P1_FN_META;
  377.                 (void)write(mfd, cmdbuf, sizeof cmdbuf);
  378.                 *cp &= ~META;
  379.             }
  380.             i = -1;
  381.             if (*cp == RUB || *cp < ' ') {
  382.                 i = protocol->p_szctlch - 1;
  383.                 while (i >= 0 && protocol->p_ctlch[i] != *cp)
  384.                     i--;
  385.             }
  386.             if (i >= 0) {
  387.                 if (cq > obuf) {
  388.                     (void)write(mfd, obuf, cq-obuf);
  389.                     cq = obuf;
  390.                 }
  391.                 cmdbuf[1] = P1_DIR_HTOM|P1_FN_CTLCH|i;
  392.                 (void)write(mfd, cmdbuf, sizeof cmdbuf);
  393.             } else {
  394.                 *cq++ = *cp;
  395.                 if (cq >= obuf+sizeof obuf) {
  396.                     (void)write(mfd, obuf, cq-obuf);
  397.                     cq = obuf;
  398.                 }
  399.             }
  400.         }
  401.         if (cq > obuf)
  402.             (void)write(mfd, obuf, cq-obuf);
  403.     } else
  404.         (void)read(w->w_datafd, ibuf, sizeof ibuf);
  405. }
  406.  
  407. static
  408. void
  409. p1_recv(mfd, cbuf, clen)
  410. fildes_t mfd;
  411. char *cbuf;
  412. int clen;
  413. {
  414.     register int len;
  415.     register char *buf, *cp, *cq;
  416.     register struct window *w;
  417.     nwin_t wnum;
  418.     auto int nready;
  419.     char ibuf[512], obuf[512];
  420.     static int seen_iac, seen_meta;
  421.     static pnreq_t pnrq_cmd;
  422.     static char cmdbuf[2] = { P1_IAC };
  423.  
  424.     /*
  425.      * The received bytestream is examined.  Non-command bytes are
  426.      * written to the file descriptor corresponding to the current
  427.      * "input" window (relative to the Macintosh -- the window the
  428.      * user types input to).
  429.      *
  430.      * If "clen" is nonzero, then the contents of the buffer "cbuf"
  431.      * are processed before any input is read.
  432.      */
  433.     if (ioctl(mfd, (int)FIONREAD, (char *)&nready) < 0) {
  434.         perror("FIONREAD");
  435.         return;
  436.     }
  437.     nready += clen;
  438.  
  439.     for (cq = obuf; nready > 0; nready -= len) {
  440.         if (clen > 0) {
  441.             len = clen;
  442.             buf = cbuf;
  443.             clen = 0;
  444.         } else {
  445.             if (nready > sizeof ibuf)
  446.                 len = read(mfd, ibuf, sizeof ibuf);
  447.             else
  448.                 len = read(mfd, ibuf, nready);
  449.             if (len <= 0) {
  450.                 perror("read");
  451.                 return;
  452.             }
  453.             buf = ibuf;
  454.         }
  455.         for (cp=buf; cp < buf+len; cp++) {
  456.             if (pnrq_cmd) {
  457.                 pcl_haggle(mfd, pnrq_cmd|*cp);
  458.                 pnrq_cmd = 0;
  459.                 /* pcl_haggle may have changed the protocol */
  460.                 if (protocol != pcl_table) {
  461.                     if (protocol->p_recv)
  462.                         (*protocol->p_recv)(mfd,
  463.                             cp+1, buf+len-cp-1);
  464.                     return;
  465.                 }
  466.             } else if (seen_iac) {
  467.                 if ((*cp&P1_DIR) == P1_DIR_MTOH) {
  468.                     if (cq > obuf) {
  469.                         (void)write(curwin.in->w_datafd,
  470.                                 obuf, cq-obuf);
  471.                         cq = obuf;
  472.                     }
  473.                     switch (*cp & P1_FN) {
  474.                     case P1_FN_NEWW:
  475.                         wnum = *cp & P1_WINDOW;
  476.                         if (!wnum)
  477.                             break;
  478.                         w = WIN_PTR(wnum);
  479.                         if (w->w_alloc)
  480.                             break;
  481.                         if (!win_neww(WC_INTERNAL,
  482.                             defwtype, wnum,
  483.                             protocol->p_maxwin, 0L,
  484.                             (fildes_t)-1, (fildes_t)-1,
  485.                             (struct woptdefn *)0)) {
  486.                             cmdbuf[1] = P1_DIR_HTOM|
  487.                                 P1_FN_KILLW|wnum;
  488.                             (void)write(mfd, cmdbuf,
  489.                                 sizeof cmdbuf);
  490.                         }
  491.                         break;
  492.                     case P1_FN_KILLW:
  493.                         wnum = *cp & P1_WINDOW;
  494.                         if (!wnum)
  495.                             break;
  496.                         win_killw(WIN_PTR(wnum));
  497.                         break;
  498.                     case P1_FN_ISELW:
  499.                         wnum = *cp & P1_WINDOW;
  500.                         if (!wnum)
  501.                             break;
  502.                         w = WIN_PTR(wnum);
  503.                         if (w->w_alloc)
  504.                             curwin.in = w;
  505.                         else
  506.                             curwin.in = NULL;
  507.                         break;
  508.                     case P1_FN_META:
  509.                         seen_meta = 1;
  510.                         break;
  511.                     case P1_FN_CTLCH:
  512.                         *cq = protocol->p_ctlch[*cp&P1_CC];
  513.                         if (seen_meta) {
  514.                             seen_meta = 0;
  515.                             *cq |= META;
  516.                         }
  517.                         if (curwin.in)
  518.                             cq++;
  519.                         break;
  520.                     case P1_FN_MAINT:
  521.                         switch (*cp & P1_MF) {
  522.                         case P1_MF_ENTRY:
  523.                             (*protocol->p_renew)(mfd);
  524.                             break;
  525.                         case P1_MF_ASKPCL:
  526.                             pcl_haggle(mfd,PNRQ_ASK);
  527.                             break;
  528.                         case P1_MF_CANPCL:
  529.                             pnrq_cmd = PNRQ_CAN;
  530.                             break;
  531.                         case P1_MF_SETPCL:
  532.                             pnrq_cmd = PNRQ_SET;
  533.                             break;
  534.                         case P1_MF_EXIT:
  535.                             done(0);
  536.                             break;
  537.                         }
  538.                         break;
  539.                     }
  540.                 }
  541.                 seen_iac = 0;
  542.             } else if (*cp == P1_IAC)
  543.                 seen_iac++;
  544.             else {
  545.                 if (seen_meta) {
  546.                     seen_meta = 0;
  547.                     *cq = *cp | META;
  548.                 } else
  549.                     *cq = *cp;
  550.                 if (curwin.in) {
  551.                     if (++cq >= obuf+sizeof obuf) {
  552.                         (void)write(curwin.in->w_datafd,
  553.                                 obuf, cq-obuf);
  554.                         cq = obuf;
  555.                     }
  556.                 }
  557.             }
  558.         }
  559.     }
  560.     if (cq > obuf)
  561.         (void)write(curwin.in->w_datafd, obuf, cq-obuf);
  562. }
  563.  
  564. static
  565. void
  566. p1_askpcl(mfd)
  567. fildes_t mfd;
  568. {
  569.     static char cmdbuf[2] = { P1_IAC,P1_DIR_HTOM|P1_FN_MAINT|P1_MF_ASKPCL };
  570.  
  571.     (void)write(mfd, cmdbuf, sizeof cmdbuf);
  572. }
  573.  
  574. static
  575. void
  576. p1_canpcl(mfd, pname)
  577. fildes_t mfd;
  578. char pname;
  579. {
  580.     static char cmdbuf[3] = { P1_IAC,P1_DIR_HTOM|P1_FN_MAINT|P1_MF_CANPCL };
  581.  
  582.     cmdbuf[2] = pname;
  583.     (void)write(mfd, cmdbuf, sizeof cmdbuf);
  584. }
  585.  
  586. static
  587. void
  588. p1_setpcl(mfd, pname)
  589. fildes_t mfd;
  590. char pname;
  591. {
  592.     static char cmdbuf[3] = { P1_IAC,P1_DIR_HTOM|P1_FN_MAINT|P1_MF_SETPCL };
  593.  
  594.     cmdbuf[2] = pname;
  595.     (void)write(mfd, cmdbuf, sizeof cmdbuf);
  596. }
  597.  
  598. static
  599. void
  600. p1_renew(mfd)
  601. fildes_t mfd;
  602. {
  603.     register struct window *w;
  604.     static char cmdbuf[2] = { P1_IAC };
  605.  
  606.     /*
  607.      * Re-init (re-NEW) an existing connection.  Send a NEWW command
  608.      * for each existing window.  This function is invoked when the
  609.      * Macintosh sends an ENTRY maintenance command.
  610.      */
  611.     for (w=window; w < window+protocol->p_maxwin; w++) {
  612.         if (w->w_alloc) {
  613.             win_renew(w, 0);
  614.             cmdbuf[1] = P1_DIR_HTOM|P1_FN_NEWW|WIN_NUM(w);
  615.             (void)write(mfd, cmdbuf, sizeof cmdbuf);
  616.         }
  617.     }
  618. }
  619.  
  620. static
  621. void
  622. p2_renew(mfd)
  623. fildes_t mfd;
  624. {
  625.     register struct window *w;
  626.     static char cmdbuf[3] = { P2_IAC };
  627.  
  628.     /*
  629.      * Re-init (re-NEW) an existing connection.  Send a NEWW command
  630.      * for each existing window.  This function is invoked when the
  631.      * Macintosh sends an ENTRY maintenance command.
  632.      */
  633.     for (w=window; w < window+protocol->p_maxwin; w++) {
  634.         if (w->w_alloc) {
  635.             win_renew(w, 1);
  636.             cmdbuf[1] = P2_DIR_HTOM|P2_FN_NEWW|WIN_NUM(w);
  637.             cmdbuf[2] = w->w_type + ' ';
  638.             (void)write(mfd, cmdbuf, sizeof cmdbuf);
  639.         }
  640.     }
  641. }
  642.  
  643. static
  644. struct window *
  645. p2_neww(mfd, wclass, wtype, wnum, wid, datafd, ctlfd)
  646. fildes_t mfd;
  647. wclass_t wclass;
  648. wtype_t wtype;
  649. nwin_t wnum;
  650. long wid;
  651. fildes_t datafd;
  652. fildes_t ctlfd;
  653. {
  654.     register struct window *w;
  655.     static char cmdbuf[3] = { P2_IAC };
  656.  
  657.     /*
  658.      * Create a new window as requested by the host.  This routine is not
  659.      * called when the Macintosh creates a window.
  660.      */
  661.     w = win_neww(wclass, wtype, wnum, protocol->p_maxwin, wid,
  662.         datafd, ctlfd, (struct woptdefn *)0);
  663.     if (w) {
  664.         cmdbuf[1] = P2_DIR_HTOM|P2_FN_NEWW|WIN_NUM(w);
  665.         cmdbuf[2] = ' ' + wtype;
  666.         (void)write(mfd, cmdbuf, sizeof cmdbuf);
  667.     }
  668.     return(w);
  669. }
  670.  
  671. static
  672. void
  673. p2_chkopt(mfd)
  674. fildes_t mfd;
  675. {
  676.     register struct window *w;
  677.     nwin_t maxwin;
  678.  
  679.     /*
  680.      * Ideally, this routine would call a routine in the window
  681.      * module (perhaps win_chkopt()), passing the maximum window
  682.      * number as one argument.  The "for" loop would be in that
  683.      * routine.  However, I'm not willing to accept the overhead
  684.      * for that conceptual nicety.
  685.      */
  686.     maxwin = protocol->p_maxwin;
  687.     for (w=window; w < window+maxwin; w++)
  688.         if (w->w_alloc)
  689.             opt_scan((caddr_t)w, &w->w_optdefn, p2_sendopt, mfd,
  690.                 P2_FN_WOPT|WIN_NUM(w));
  691. }
  692.  
  693. static
  694. void
  695. p2_sendopt(mfd, fn, buf, len)
  696. fildes_t mfd;
  697. int fn;
  698. register char *buf;
  699. register int len;
  700. {
  701.     register char *cp;
  702.     register int i;
  703.     char outbuf[512];
  704.  
  705.     /*
  706.      * Encode and transmit the option string contained in "buf".  The
  707.      * initial command (which will be P2_FN_WOPT|WIN_NUM(w)) is
  708.      * contained in "fn".
  709.      *
  710.      * The caller is responsible for handing us a correctly-formed
  711.      * option string.  This routine merely performs the protocol encoding
  712.      * which is required for control and meta characters.
  713.      */
  714.     curwin.out = NULL;
  715.     outbuf[0] = P2_IAC;
  716.     outbuf[1] = fn|P2_DIR_HTOM;
  717.     for (cp=outbuf+2; len > 0; buf++,len--) {
  718.         if (cp > outbuf+sizeof outbuf - 4) {
  719.             (void)write(mfd, outbuf, cp-outbuf);
  720.             cp = outbuf;
  721.         }
  722.         if (*buf & META) {
  723.             *cp++ = P2_IAC;
  724.             *cp++ = P2_DIR_HTOM|P2_FN_META;
  725.             *buf &= ~META;
  726.         }
  727.         i = -1;
  728.         if (*buf == RUB || *buf < ' ') {
  729.             i = protocol->p_szctlch - 1;
  730.             while (i >= 0 && protocol->p_ctlch[i] != *buf)
  731.                 i--;
  732.         }
  733.         if (i >= 0) {
  734.             *cp++ = P2_IAC;
  735.             *cp++ = P2_DIR_HTOM|P2_FN_CTLCH|(i&7);
  736.         } else
  737.             *cp++ = *buf;
  738.     }
  739.     if (cp > outbuf)
  740.         (void)write(mfd, outbuf, cp-outbuf);
  741. }
  742.  
  743. static
  744. void
  745. p2_recv(mfd, cbuf, clen)
  746. fildes_t mfd;
  747. char *cbuf;
  748. int clen;
  749. {
  750.     register int len;
  751.     register char *buf, *cp, *cq;
  752.     register struct window *w;
  753.     register char c;
  754.     nwin_t wnum;
  755.     auto int nready;
  756.     char ibuf[512], obuf[512];
  757.     static int seen_iac, seen_meta, is_option;
  758.     static pnreq_t pnrq_cmd;
  759.     static nwin_t neww;
  760.     static char cmdbuf[2] = { P2_IAC };
  761.  
  762.     /*
  763.      * The received bytestream is examined.  Non-command bytes are
  764.      * written to the file descriptor corresponding to the current
  765.      * "input" window (relative to the Macintosh -- the window the
  766.      * user types input to).
  767.      *
  768.      * If "clen" is nonzero, then the contents of the buffer "cbuf"
  769.      * are processed before any input is read.
  770.      */
  771.     if (ioctl(mfd, (int)FIONREAD, (char *)&nready) < 0) {
  772.         perror("FIONREAD");
  773.         return;
  774.     }
  775.     nready += clen;
  776.  
  777.     for (cq = obuf; nready > 0; nready -= len) {
  778.         if (clen > 0) {
  779.             len = clen;
  780.             buf = cbuf;
  781.             clen = 0;
  782.         } else {
  783.             if (nready > sizeof ibuf)
  784.                 len = read(mfd, ibuf, sizeof ibuf);
  785.             else
  786.                 len = read(mfd, ibuf, nready);
  787.             if (len <= 0) {
  788.                 perror("read");
  789.                 return;
  790.             }
  791.             buf = ibuf;
  792.         }
  793.         for (cp=buf; cp < buf+len; cp++) {
  794.             if (pnrq_cmd) {
  795.                 pcl_haggle(mfd, pnrq_cmd|*cp);
  796.                 pnrq_cmd = 0;
  797.                 /* pcl_haggle may have changed the protocol */
  798.                 if (protocol != pcl_table) {
  799.                     if (protocol->p_recv)
  800.                         (*protocol->p_recv)(mfd,
  801.                             cp+1, buf+len-cp-1);
  802.                     return;
  803.                 }
  804.             } else if (neww) {
  805.                 w = WIN_PTR(neww);
  806.                 if (!w->w_alloc &&
  807.                     !win_neww(WC_INTERNAL, (wtype_t)(*cp-' '),
  808.                     neww, protocol->p_maxwin, 0L,
  809.                     (fildes_t)-1, (fildes_t)-1,
  810.                     (struct woptdefn *)0)) {
  811.                     cmdbuf[1] = P2_DIR_HTOM|
  812.                         P2_FN_KILLW|neww;
  813.                     (void)write(mfd, cmdbuf,
  814.                         sizeof cmdbuf);
  815.                 }
  816.                 neww = 0;
  817.             } else if (seen_iac) {
  818.                 if ((*cp&P2_DIR) == P2_DIR_MTOH) {
  819.                     c = *cp & P2_FN;
  820.                     if (is_option &&
  821.                         c!=P2_FN_META && c!=P2_FN_CTLCH) {
  822.                         opt_iflush();
  823.                         is_option = 0;
  824.                     }
  825.                     if (cq > obuf) {
  826.                         (void)write(curwin.in->w_datafd,
  827.                                 obuf, cq-obuf);
  828.                         cq = obuf;
  829.                     }
  830.                     switch (*cp & P2_FN) {
  831.                     case P2_FN_NEWW:
  832.                         neww = *cp & P2_WINDOW;
  833.                         break;
  834.                     case P2_FN_WOPT:
  835.                         wnum = *cp & P2_WINDOW;
  836.                         if (!wnum)
  837.                             break;
  838.                         w = WIN_PTR(wnum);
  839.                         if (!w->w_alloc) {
  840.                             curwin.in = NULL;
  841.                             break;
  842.                         }
  843.                         is_option = 1;
  844.                         opt_istart((caddr_t)w, &w->w_optdefn);
  845.                         break;
  846.                     case P2_FN_KILLW:
  847.                         wnum = *cp & P2_WINDOW;
  848.                         if (!wnum)
  849.                             break;
  850.                         win_killw(WIN_PTR(wnum));
  851.                         break;
  852.                     case P2_FN_ISELW:
  853.                         wnum = *cp & P2_WINDOW;
  854.                         if (!wnum)
  855.                             break;
  856.                         w = WIN_PTR(wnum);
  857.                         if (w->w_alloc)
  858.                             curwin.in = w;
  859.                         else
  860.                             curwin.in = NULL;
  861.                         break;
  862.                     case P2_FN_META:
  863.                         seen_meta = 1;
  864.                         if ((*cp&P2_CC) == 0)
  865.                             break;
  866.                         /* no break */
  867.                     case P2_FN_CTLCH:
  868.                         c=protocol->p_ctlch[*cp&P2_CC];
  869.                         if (seen_meta) {
  870.                             seen_meta = 0;
  871.                             c |= META;
  872.                         }
  873.                         if (is_option)
  874.                             is_option=opt_input(c);
  875.                         else
  876.                             if (curwin.in)
  877.                                 *cq++ = c;
  878.                         break;
  879.                     case P2_FN_MAINT:
  880.                         switch (*cp & P2_MF) {
  881.                         case P2_MF_ENTRY:
  882.                             (*protocol->p_setpcl)(mfd, protocol->p_name);
  883.                             (*protocol->p_renew)(mfd);
  884.                             break;
  885.                         case P2_MF_ASKPCL:
  886.                             pcl_haggle(mfd,PNRQ_ASK);
  887.                             break;
  888.                         case P2_MF_CANPCL:
  889.                             pnrq_cmd = PNRQ_CAN;
  890.                             break;
  891.                         case P2_MF_SETPCL:
  892.                             pnrq_cmd = PNRQ_SET;
  893.                             break;
  894.                         case P2_MF_EXIT:
  895.                             done(0);
  896.                             break;
  897.                         }
  898.                         break;
  899.                     }
  900.                 }
  901.                 seen_iac = 0;
  902.             } else if (*cp == P2_IAC)
  903.                 seen_iac++;
  904.             else {
  905.                 if (seen_meta) {
  906.                     c = *cp | META;
  907.                     seen_meta = 0;
  908.                 } else
  909.                     c = *cp;
  910.                 if (is_option)
  911.                     is_option = opt_input(c);
  912.                 else
  913.                     if (curwin.in)
  914.                         *cq++ = c;
  915.                 if (cq >= obuf+sizeof obuf) {
  916.                     (void)write(curwin.in->w_datafd,
  917.                             obuf, cq-obuf);
  918.                     cq = obuf;
  919.                 }
  920.             }
  921.         }
  922.     }
  923.     if (cq > obuf)
  924.         (void)write(curwin.in->w_datafd, obuf, cq-obuf);
  925. }
  926.